<?php
/** This class contains basic DB functions.
 */
class Db
{
	var $history = array();
	var $_link    = 0;
	var $debug    = true;

   /** Empty constructor */
	function Db() { }


   /** Returns an instance of Database class
	*  @access public
	*  @param array $conf is a hash with (name, host, user, pass, debug)
	*  @return Db object ( already connected)
	*/
	static function get($conf)
	{
		static $instances = array();

		$k = serialize($conf);
		if (! isSet($instances[$k]))
		{
			$instances[$k] = new Db();
			$instances[$k]->debug = $conf['debug'];
			$instances[$k]->connect($conf['host'], $conf['name'], $conf['user'], $conf['pass']);
			$instances[$k]->query('SET @@collation_connection = @@collation_database');
		}
		return $instances[$k];
	}


	/** Connects to database server and selects db.
	 * @access public
	 * @param string $host database host
	 * @param string $name database name
	 * @param string $user user name
	 * @param string $pass user password
	 * @return boolean
	 */
	function connect($host, $name, $user, $pass)
	{
		$this->_link = mysql_pconnect($host, $user, $pass);
		if (! $this->_link)
			IError::fatal('Unable to connect to db server.');
		if (! mysql_select_db($name, $this->_link))
			IError::fatal('Unable to connect to db.');
		$this->query('SET NAMES utf8');
		return true;
	}

	/** Makes sql query with error reporting and logging.
	 * @access public
	 * @param  string $sql query string
	 * @return int query result handle
	 */
	function query($sql)
	{
		if ($this->debug)
		{
			list($usec, $sec) = explode(' ', microtime());
			$start =  ((float)$usec + (float)$sec);
		}

		$res = mysql_query($sql, $this->_link);

		if ($this->debug)
		{
			list($usec, $sec) = explode(' ', microtime());
			$finish =  ((float)$usec + (float)$sec);
			$this->history[] = array('sql'=>$sql, 'time'=>($finish-$start));
		}
		if (!$res)
			IError::fatal('SQL: '.$sql."\n\ MYSQL: ".$this->error());

		return $res;
	}

	/** Returns row id from last executed query
	 * @return int id of last INSERT operation
	 */
	function getLastID()
	{
		return mysql_insert_id($this->_link);
	}



	/** Prepares string to store in db .
	 * @param mixed $mixed string or array of string need to be escaped
	 * @return mixed escaped string or array of escaped strings
	 */
	function escape($mixed)
	{
		if (is_array($mixed))
			return array_map(array($this, 'escape'), $mixed);
		$mixed = mysql_real_escape_string($mixed, $this->_link);
		return $mixed;
	}



	/** Gets data by sql query
	 * @param string $sql    select query
	 * @param string $isAssoc  type of returned rows array
	 * @param string $keyField  if given, function returns results like $rows[$row[$keyField]] = $row;
	 * @return array selected rows (each row is array of specified type) or emprt array on error
	 */
	function getRows($sql, $isAssoc=true, $keyField='')
	{
		$rows = array();
		$assocType = ($isAssoc ? MYSQL_ASSOC : MYSQL_NUM);

		$res = $this->query($sql);
		while ($row = mysql_fetch_array($res, $assocType))
		{
			if ($keyField)
				$rows[$row[$keyField]] = $row; //add with given key
			else
				$rows[] = $row; //add with default keys
		}
		return $rows; //empty array on error
	}

	/** Returns exactly one row as array. If there is number of rows
	 * satisfying the condition then the first one will be returned.
	 * @param string $sql   select query
	 * @param bool $isAssoc  type of returned rows array (assoc or numeric keys)
	 * @return array exact one row (first if multiply row selected):
	 *                 or false on error
	 * @see getRows()
	 */
	function getRow($sql, $isAssoc = true)
	{
		$res = $this->query($sql);
		return mysql_fetch_array($res, ($isAssoc ? MYSQL_ASSOC : MYSQL_NUM));
	}

	/** Makes hash array from two columns 'col1'=>'col2'
	 * @param string $sql sql query
	 * @return array hash array with keys from first column
	 *               and values from second
	 */
	function getHash($sql)
	{
		$rows = $this->getRows($sql, false);
		$res = array();
		foreach ($rows as $val)
			$res[$val[0]] = $val[1];
		return $res; //empty array on error
	}

	/** Returns one field from a row
	 * @param string $sql SQL query
	 * @return string field value
	 */
	function getField($sql)
	{
		$res = '';
		$row = $this->getRow($sql, false);
		if ($row)
			$res = $row[0];
		return $res;
	}

	/** Makes array from one column, usefull for selecting ids for sql IN statement
	 * @param string $sql sql query
	 * @param bool $addZero add 0 to empty array
	 * @return array with columns if any or array(0) othervise.
	 */
	function getFields($sql, $addZero=true)
	{
		$rows = $this->getRows($sql, false);
		$res = array();
		foreach ($rows as $val)
			$res[] = $val[0];
		if (!$res && $addZero)
			$res[] = 0;
		return $res;
	}


	/** Returns number of rows returned by a given SQL query
	 * Use this  function only(!) if query  'SELECT COUNT(*) ...' is impossible
	 * @param string $sql sql query
	 * @return int rows count
	 */
	function countRows($sql)
	{
		$res = $this->query($sql);
		return mysql_num_rows($res);
	}

	/** Performs insert of one row. Accepts values to insert as an array:
	 *    'column1' => 'value1'
	 *    'column2' => 'value2'
	 *    ...
	 * @param string  $table    table name
	 * @param array   $values   column and values to insert
	 * @param boolean $escape true - method escapes values (with "), false - not escapes
	 * @return int last ID (or 0 on error)
	 */
	function insert($table, $values, $escape=true)
	{
		$cols = implode(',', array_keys($values));
		if ($escape)
			$vals = '"'.implode('","', array_values($values)).'"';
		else
			$vals = implode(',', array_values($values));

		$sql = 'INSERT INTO '.$table.' ('.$cols.')'.' VALUES ('.$vals.')';
		if($this->query($sql))
			return $this->getLastID();
		return 0;
	}

	/** Performs update of rows.
	 * @param string $table  table name
	 * @param array  $values array of column=>new_value
	 * @param string $where   condition (without word WHERE)
	 * @param boolean $escape true - method escapes values (with "), false - not escapes
	 * @return boolean true - update successfule, false - error
	 */
	function update($table, $values, $where, $escape=true)
	{
		if (!is_array($values) || empty($values))
			return false;
		$pairs = '';
		foreach ($values as $col=>$val)
		{
			if ($escape)
				$pairs .= $col.'="'.$val.'",';
			else
				$pairs .= $col.'='.$val.',';
		}
		$pairs[strlen($pairs)-1]=' '; //replace trailing ','
		$sql = 'UPDATE `'.$table.'` SET '.$pairs.' WHERE '.$where;
		return $this->query($sql);
	}

	/** Formats date as string for MySQL
	 * @param int $timestamp datetime as timestamp (current time if omitted)
	 * @return string fomratted for mysql datetime
	 */
	function date($timestamp = 0) {
		$timestamp = $timestamp ? $timestamp : time();
		return date('Y-m-d H:i:s', $timestamp);
	}

	/**
	 * Gets last error of current connection
	 *
	 * @return string error message
	 */
	function error()
	{
		return mysql_error($this->_link);
	}
}
?>